using System;
using System.Reflection;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Leo.Core.DevExtreme
{
    public class DevExtremeFilterModelBinder : IModelBinder
    {
        static readonly System.Text.RegularExpressions.Regex _jsonValid = new System.Text.RegularExpressions.Regex("^(\\{|\\[).*(\\}|\\])$");
        static readonly System.Text.RegularExpressions.Regex _intValid = new System.Text.RegularExpressions.Regex("^(\\-)[0-9]+$");
        static readonly System.Text.RegularExpressions.Regex _numberValid = new System.Text.RegularExpressions.Regex("^(\\-)[0-9]+\\.[0-9]+$");

        public DevExtremeFilterModelBinder()
        {
        }

        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            var modelName = bindingContext.ModelName;

            // Try to fetch the value of the argument by name
            var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
            System.Collections.IList result;

            if (valueProviderResult == ValueProviderResult.None)
            {
                result = null;
            }
            else
            {
                result = new System.Collections.Generic.List<object>();

                foreach (var valueTemp in valueProviderResult)
                {
                    if (!string.IsNullOrWhiteSpace(valueTemp))
                    {
                        if (_intValid.IsMatch(valueTemp))
                        {
                            result.Add(int.Parse(valueTemp));
                        }
                        else if (_numberValid.IsMatch(valueTemp))
                        {
                            result.Add(double.Parse(valueTemp));
                        }
                        else if (_jsonValid.IsMatch(valueTemp))
                        {
                            var jtokenVal = Newtonsoft.Json.Linq.JToken.Parse(valueTemp);
                            result.Add(GetJtokenValue(jtokenVal));
                        }
                        else
                        {
                            result.Add(valueTemp);
                        }
                    }
                    else
                    {
                        result.Add(valueTemp);
                    }
                }

                bindingContext.Result = ModelBindingResult.Success(result);
            }

            return Task.CompletedTask;
        }

        private object GetJtokenValue(Newtonsoft.Json.Linq.JToken jTokenValue)
        {
            switch (jTokenValue.Type)
            {
                case Newtonsoft.Json.Linq.JTokenType.Array:
                    var result = new System.Collections.Generic.List<object>();

                    foreach (var child in jTokenValue)
                    {
                        result.Add(GetJtokenValue(child));
                    }

                    return result;
                case Newtonsoft.Json.Linq.JTokenType.None:
                case Newtonsoft.Json.Linq.JTokenType.Null:
                    return null;
                case Newtonsoft.Json.Linq.JTokenType.Boolean:
                    return bool.Parse(jTokenValue.ToString());
                case Newtonsoft.Json.Linq.JTokenType.Integer:
                    return int.Parse(jTokenValue.ToString());
                case Newtonsoft.Json.Linq.JTokenType.Float:
                    return double.Parse(jTokenValue.ToString());
                default:
                    return jTokenValue.ToString();
            }
        }
    }
}
